package main

import (
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"ipaDistribute/util"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/skip2/go-qrcode"
)

// 配置信息
var config = struct {
	Port    int    `json:"port"`
	Domain  string `json:"domain"`
	DataDir string `json:"dataDir"`
}{
	Port:    8080,
	Domain:  "https://yourdomain.com", // 替换为实际域名
	DataDir: "./data",
}

// 应用元数据结构
type AppMeta struct {
	ID            string `json:"id"`
	BundleID      string `json:"bundleId" binding:"required"`
	BundleVersion string `json:"bundleVersion" binding:"required"`
	BundleTag     string `json:"bundleTag" binding:"required"`
	Title         string `json:"title" binding:"required"`
	Kind          string `json:"kind"`
	UpdateInfo    string `json:"updateInfo"`
	IPAPath       string `json:"ipaPath"`
	CreatedAt     int64  `json:"createdAt"`
	UpdatedAt     int64  `json:"updatedAt"`
}

type AppStorage struct {
	Apps []AppMeta `json:"apps"`
}

var storage AppStorage

func main() {
	// 初始化存储目录
	if err := os.MkdirAll(config.DataDir, 0755); err != nil {
		log.Fatalf("创建存储目录失败: %v", err)
	}
	if err := os.MkdirAll(filepath.Join(config.DataDir, "ipa"), 0755); err != nil {
		log.Fatalf("创建IPA存储目录失败: %v", err)
	}

	// 加载配置
	configFile := "config.json"
	if len(os.Args) > 1 {
		configFile = os.Args[1]
	}
	if err := loadConfig(configFile); err != nil {
		return
	}

	// 加载元数据
	if err := loadMetadata(); err != nil {
		return
	}

	// 创建函数映射
	funcMap := template.FuncMap{
		"formatTime": formatTime, // 注册模板函数
	}
	r := gin.Default()
	r.SetFuncMap(funcMap) // 关键步骤！
	r.LoadHTMLGlob("templates/*")

	// 设置路由
	r.GET("/", listHandler)
	r.GET("/apps", listHandler)
	r.POST("/upload", uploadHandler)
	r.GET("/app/:id", downloadPageHandler)
	r.GET("/plist/:id", generatePlistHandler)
	r.GET("/download/:id", downloadHandler)
	r.POST("/delete/:id", deleteHandler)
	r.POST("/update/:id", updateHandler)
	// 添加上传页面路由
	r.GET("/upload", func(c *gin.Context) {
		c.HTML(http.StatusOK, "upload.html", nil)
	})
	fmt.Printf("服务器启动: http://localhost:%d\n", config.Port)
	if err := r.Run(fmt.Sprintf(":%d", config.Port)); err != nil {
		log.Fatalf("服务器启动失败: %v", err)
	}
}

// 文件上传处理
func uploadHandler(c *gin.Context) {
	file, err := c.FormFile("ipaFile")
	if err != nil {
		log.Printf("获取文件失败：%v", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "获取文件失败"})
		return
	}

	// 验证文件类型
	if !strings.HasSuffix(strings.ToLower(file.Filename), ".ipa") {
		log.Printf("仅支持IPA文件：%s", file.Filename)
		c.JSON(http.StatusBadRequest, gin.H{"error": "仅支持IPA文件"})
		return
	}

	id := c.PostForm("ipaId")
	bundleTag := c.PostForm("bundleTag")
	if id == "" {
		// 创建新记录
		id = generateID(bundleTag)
	}
	tmpFile := filepath.Join(os.TempDir(), id+".ipa")
	if err := c.SaveUploadedFile(file, tmpFile); err != nil {
		log.Printf("文件保存失败：%v", err)
		c.HTML(http.StatusOK, "upload.html", gin.H{
			"AppInfo": AppMeta{
				ID:         id,
				BundleTag:  bundleTag,
				Title:      c.PostForm("title"),
				Kind:       c.PostForm("kind"),
				UpdateInfo: c.PostForm("updateInfo"),
			},
			"ErrMsg": "文件保存失败",
		})
		return
	}

	// 读取ipaInfo
	ipaInfo, err := util.ExtractIPAInfo(tmpFile)
	if err != nil {
		log.Printf("无法读取ipa数据：%v", err)
		c.HTML(http.StatusOK, "upload.html", gin.H{
			"AppInfo": AppMeta{
				ID:         id,
				BundleTag:  bundleTag,
				Title:      c.PostForm("title"),
				Kind:       c.PostForm("kind"),
				UpdateInfo: c.PostForm("updateInfo"),
			},
			"ErrMsg": "无法读取ipa数据",
		})
		return
	}

	// 检查是否已存在
	if exists, app := checkAppExists(ipaInfo.BundleID, bundleTag); exists {
		// 更新现有记录
		app.Title = c.PostForm("title")
		app.Kind = c.PostForm("kind")
		app.UpdateInfo = c.PostForm("updateInfo")
		app.UpdatedAt = time.Now().Unix()

		// 保存新IPA文件
		dst := filepath.Join(config.DataDir, "ipa", app.ID+".ipa")
		if err := MoveFile(tmpFile, dst); err != nil {
			log.Printf("文件移动失败：%v", err)
			c.HTML(http.StatusOK, "upload.html", gin.H{
				"AppInfo": app,
				"ErrMsg":  "文件移动失败",
			})
			return
		}

		saveMetadata()
		c.HTML(http.StatusOK, "upload.html", gin.H{
			"AppInfo": app,
			"ErrMsg":  "更新成功",
		})
		return
	}

	// 保存新IPA文件
	dst := filepath.Join(config.DataDir, "ipa", id+".ipa")
	if err := MoveFile(tmpFile, dst); err != nil {
		log.Printf("文件保存失败：%v", err)
		c.HTML(http.StatusOK, "upload.html", gin.H{
			"AppInfo": AppMeta{
				ID:         id,
				BundleTag:  bundleTag,
				Title:      c.PostForm("title"),
				Kind:       c.PostForm("kind"),
				UpdateInfo: c.PostForm("updateInfo"),
			},
			"ErrMsg": "文件保存失败",
		})
		return
	}

	newApp := AppMeta{
		ID:            id,
		BundleID:      ipaInfo.BundleID,
		BundleVersion: ipaInfo.BuildVersion,
		BundleTag:     bundleTag,
		Title:         c.PostForm("title"),
		Kind:          c.PostForm("kind"),
		UpdateInfo:    c.PostForm("updateInfo"),
		IPAPath:       dst,
		CreatedAt:     time.Now().Unix(),
		UpdatedAt:     time.Now().Unix(),
	}
	storage.Apps = append(storage.Apps, newApp)
	saveMetadata()
	c.HTML(http.StatusOK, "upload.html", gin.H{
		"AppInfo": newApp,
		"ErrMsg":  "更新成功",
	})
}

// 生成Plist文件
func generatePlistHandler(c *gin.Context) {
	id := c.Param("id")
	app, exists := getAppByID(id)
	if !exists {
		c.AbortWithStatus(http.StatusNotFound)
		return
	}

	plist := fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>items</key>
    <array>
        <dict>
            <key>assets</key>
            <array>
                <dict>
                    <key>kind</key>
                    <string>software-package</string>
                    <key>url</key>
                    <string>%s/download/%s</string>
                </dict>
            </array>
            <key>metadata</key>
            <dict>
                <key>bundle-identifier</key>
                <string>%s</string>
                <key>bundle-version</key>
                <string>%s</string>
                <key>kind</key>
                <string>%s</string>
                <key>title</key>
                <string>%s</string>
            </dict>
        </dict>
    </array>
</dict>
</plist>`, config.Domain, id, app.BundleID, app.BundleVersion, app.Kind, app.Title)

	c.Header("Content-Type", "application/xml")
	c.String(http.StatusOK, plist)
}

// 查询列表
func listHandler(c *gin.Context) {
	bundleID := c.Query("bundleId")
	version := c.Query("bundleVersion")
	title := c.Query("title")

	results := []AppMeta{}
	for _, app := range storage.Apps {
		if (bundleID == "" || app.BundleID == bundleID) &&
			(version == "" || app.BundleVersion == version) &&
			(title == "" || strings.Contains(strings.ToLower(app.Title), strings.ToLower(title))) {
			results = append(results, app)
		}
	}

	if c.Request.Header.Get("Accept") == "application/json" {
		c.JSON(http.StatusOK, results)
	} else {
		c.HTML(http.StatusOK, "list.html", gin.H{
			"bundleId":      bundleID,
			"bundleVersion": version,
			"title":         title,
			"Apps":          results,
		})
	}
}

// 下载页面
func downloadPageHandler(c *gin.Context) {
	id := c.Param("id")
	app, exists := getAppByID(id)
	if !exists {
		c.AbortWithStatus(http.StatusNotFound)
		return
	}

	// 生成二维码
	installURL := fmt.Sprintf("%s/app/%s", config.Domain, id)
	qr, err := qrcode.Encode(installURL, qrcode.Medium, 256)
	if err != nil {
		c.AbortWithStatus(http.StatusInternalServerError)
		return
	}
	qrBase64 := base64.StdEncoding.EncodeToString(qr)

	c.HTML(http.StatusOK, "download.html", gin.H{
		"Title":        app.Title,
		"Version":      app.BundleVersion,
		"Tag":          app.BundleTag,
		"UpdateInfo":   app.UpdateInfo,
		"plistURL":     template.URL(fmt.Sprintf("%s/plist/%s", config.Domain, id)),
		"QRCodeBase64": qrBase64,
	})
}

// IPA文件下载
func downloadHandler(c *gin.Context) {
	id := c.Param("id")
	if app, exists := getAppByID(id); exists {
		c.Header("Content-Type", "application/octet-stream")
		c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s.ipa", app.Title))
		c.Header("Content-Transfer-Encoding", "binary")
		c.File(app.IPAPath)
	} else {
		c.AbortWithStatus(http.StatusNotFound)
	}
}

// 删除应用
func deleteHandler(c *gin.Context) {
	id := c.Param("id")
	for i, app := range storage.Apps {
		if app.ID == id {
			// 删除文件
			if err := os.Remove(app.IPAPath); err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"error": "删除文件失败"})
				return
			}

			// 删除记录
			storage.Apps = append(storage.Apps[:i], storage.Apps[i+1:]...)
			saveMetadata()
			c.JSON(http.StatusOK, gin.H{"status": "deleted"})
			return
		}
	}
	c.JSON(http.StatusNotFound, gin.H{"error": "应用未找到"})
}

// 更新应用信息
func updateHandler(c *gin.Context) {
	id := c.Param("id")
	for i, app := range storage.Apps {
		if app.ID == id {
			storage.Apps[i].Title = c.PostForm("title")
			storage.Apps[i].UpdateInfo = c.PostForm("updateInfo")
			storage.Apps[i].Kind = c.PostForm("kind")
			storage.Apps[i].UpdatedAt = time.Now().Unix()
			saveMetadata()
			c.HTML(http.StatusOK, "upload.html", gin.H{
				"AppInfo": storage.Apps[i],
				"ErrMsg":  "成功完成更新",
			})
			return
		}
	}
	c.HTML(http.StatusOK, "upload.html", gin.H{
		"AppInfo": AppMeta{
			ID:            id,
			Title:         c.PostForm("title"),
			UpdateInfo:    c.PostForm("updateInfo"),
			Kind:          c.PostForm("kind"),
			BundleID:      c.PostForm("bundleId"),
			BundleVersion: c.PostForm("bundleVersion"),
			BundleTag:     c.PostForm("bundleTag"),
		},
		"ErrMsg": "更新失败",
	})
}

func loadConfig(path string) (err error) {
	data, err := os.ReadFile(path)
	if err != nil {
		log.Printf("加载配置文件失败: %v", err)
		return
	}
	if err = json.Unmarshal(data, &config); err != nil {
		log.Printf("解析配置文件失败: %v", err)
	}
	return
}

// 辅助函数
func loadMetadata() (err error) {
	data, err := os.ReadFile(filepath.Join(config.DataDir, "metadata.json"))
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		log.Printf("加载元数据失败: %v", err)
		return
	}
	if err := json.Unmarshal(data, &storage); err != nil {
		log.Printf("解析元数据失败: %v", err)
	}
	return
}

func saveMetadata() {
	data, err := json.MarshalIndent(storage, "", "  ")
	if err != nil {
		log.Printf("序列化元数据失败: %v", err)
		return
	}
	if err := os.WriteFile(filepath.Join(config.DataDir, "metadata.json"), data, 0644); err != nil {
		log.Printf("保存元数据失败: %v", err)
	}
}

func checkAppExists(bundleID, tag string) (bool, *AppMeta) {
	for i, app := range storage.Apps {
		if app.BundleID == bundleID && app.BundleTag == tag {
			return true, &storage.Apps[i]
		}
	}
	return false, nil
}

func getAppByID(id string) (*AppMeta, bool) {
	for _, app := range storage.Apps {
		if app.ID == id {
			return &app, true
		}
	}
	return nil, false
}

func generateID(seed string) string {
	hash := sha256.Sum256([]byte(seed + time.Now().String()))
	return fmt.Sprintf("%x", hash[:8])
}

// 新增自定义函数：时间格式化
func formatTime(timestamp int64) string {
	t := time.Unix(timestamp, 0)
	return t.Format("2006-01-02 15:04:05") // Go的固定时间格式模板
}

// 移动文件
func MoveFile(src, dst string) (err error) {
	in, err := os.Open(src)
	if err != nil {
		log.Printf("源文件[%s]打开异常：%v", src, err)
		return
	}
	defer func() {
		in.Close()
		if err != nil {
			err = os.Remove(src)
		}
	}()

	out, err := os.Create(dst)
	if err != nil {
		log.Printf("目标文件[%s]打开异常：%v", dst, err)
		return
	}
	defer func() {
		if e := out.Close(); e != nil {
			err = e
		}
	}()

	_, err = io.Copy(out, in)
	if err != nil {
		log.Printf("复制文件内容时发送错误：%v", err)
		return
	}

	// Don't forget to flush the file buffer before closing.
	if err = out.Sync(); err != nil {
		return
	}

	return
}
